package sketch.generator;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import sketch.util.Checker;

public class ChainValueGenerator<T> extends ValueGenerator<T> {
	/**
	 * A chain of value generators
	 * */
	private List<ValueGenerator<T>> generators;
	/**
	 * The list go through the value generators
	 * */
	private Iterator<ValueGenerator<T>> iterator;
	/**
	 * The current generator
	 * */
	private ValueGenerator<T> currentGenerator;
	
	public ChainValueGenerator() {
		super();
		generators = new LinkedList<ValueGenerator<T>>();
	}
	
	public ChainValueGenerator(Collection<T> values) {
		this();
		for(T value : values) {
			add(value);
		}
	}
	
	public ChainValueGenerator(T... values) {
		this();
		this.add(values);
	}
	
	public ChainValueGenerator(ValueGenerator<T>...generators) {
		this();
		for(ValueGenerator<T> gen : generators) {
			this.addGenerator(gen);
		}
	}
	
	public ValueGenerator<T> currentGenerator() {
		return this.currentGenerator;
	}
	
	public void add(T obj) {
		this.addGenerator(new SingleValueGenerator<T>(obj));
	}
	
	public void add(T[] objs) {
		Checker.checkNull(objs, "obj array could not be null.");
		for(T obj : objs) {
			add(obj);
		}
	}
	
	public <GeneratorType extends ValueGenerator<T>> void addGenerator(GeneratorType generator) {
		this.generators.add(generator);
		this.reset();
	}

	@Override
	public T composeData() {
		throw new UnsupportedOperationException("composeData() is not supported in ChainValueGenerator yet!");
	}
	
	@Override
	public void reset() {
		super.reset();
		//XXXbug in original program???
		iterator = null;
		currentGenerator = null;
		//must reset it
		if (!generators.isEmpty()) {
			//point to the first place!
			iterator = generators.iterator();				
			for (ValueGenerator<T> gen : generators) {
				gen.reset();
			}
		}
	}

	@Override
	protected T generateNext() {
		if(!hasNext()) {
			throw new IterationException("There is no next element!");
		}
		if(currentGenerator == null) {
			throw new IterationException("The current generator is null!");
		}
		if(!currentGenerator.hasNext()) {
			throw new IterationException("The current generator does not have next().");
		}
		return currentGenerator.next();
	}

	@Override
	public boolean hasNext() {
		if(this.currentGenerator != null && this.currentGenerator.hasNext()) {
			return true;
		}
		//loop throught the generator list
		while(iterator != null && iterator.hasNext()) {
			currentGenerator = iterator.next();
			if(currentGenerator.hasNext()) {
				return true;
			}
		}
		return false;
	}

	@Override
	public List<T> nextWrappedValue() {
		T t = this.generateNext();
		List<T> wrapList = Collections.singletonList(t);
		return wrapList;
	}

}
